package com.zxy.ziems.collector.job;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.intelligt.modbus.jlibmodbus.utils.ModbusFunctionCode;
import com.zxy.ziems.collector.modbus.ModbusByteResponse;
import com.zxy.ziems.collector.modbus.ModbusModel;
import com.zxy.ziems.collector.modbus.ModbusTcpTemplate;
import com.zxy.ziems.collector.parse.Data;
import com.zxy.ziems.collector.utils.HexUtil;
import com.zxy.ziems.common.pojo.MeasureDataBean;
import com.zxy.ziems.common.mqtt.pojo.MqttMessagePayload;
import com.zxy.ziems.common.mqtt.template.MqttClientTemplate;
import com.zxy.ziems.common.pojo.collector.Unit;
import com.zxy.ziems.common.pojo.enums.DataEnum;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 采集Job
 *
 * @author 10348359
 * @create 2024-04-28 14:36
 */
@Slf4j
public class CollectorModbusJob extends QuartzJobBean {
    private static String MEASURE_TOPIC = SpringUtil.getProperty("ziems.topic.measure-data");
    ;

    private static String COLLECTOR_CODE = SpringUtil.getProperty("ziems.collector.code");

    final private String topic;

    static ConcurrentHashMap<String, String> GLOBAL_CACHE = new ConcurrentHashMap<>();

    public CollectorModbusJob() {
        this.topic = MEASURE_TOPIC + "/" + COLLECTOR_CODE;
    }

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        JobDataMap dataMap = context.getMergedJobDataMap();
        ModbusTcpTemplate template = (ModbusTcpTemplate) dataMap.get("template");
        ArrayList<ModbusModel> models = (ArrayList<ModbusModel>) dataMap.get("models");
        Map<String, Unit> unitMap = (Map<String, Unit>) dataMap.get("unitMap");
        String deviceType = dataMap.getString("deviceType");

        Map<String, ModbusByteResponse> addressDataCache = new HashMap<>();
        List<MeasureDataBean> dataList = new ArrayList<>(models.size());
        LocalDateTime time = LocalDateTimeUtil.now();
        models.forEach(model -> {
            ModbusFunctionCode functionCode = ModbusFunctionCode.get(model.getFunctionCode());
            DataEnum dataEnum = DataEnum.getBy(model.getType());
            if (Objects.isNull(dataEnum) || Objects.isNull(functionCode)) {
                return;
            }
            try {
                String addressCacheKey = model.getStation() + "-" + model.getFunctionCode() + "-" + model.getStartAddress() + "-" + model.getQuantity();
                ModbusByteResponse response;
                if (addressDataCache.containsKey(addressCacheKey)) {
                    response = addressDataCache.get(addressCacheKey);
                    log.debug("[采集器][{}]通过缓存区读取{}的数据key={}", deviceType, model.getDeviceId(), addressCacheKey);
                } else {
                    response = template.readWithReconncet(functionCode, model.getStation(), model.getStartAddress(), model.getQuantity(), 5000);
                    addressDataCache.put(addressCacheKey, response);
                    log.debug("[采集器][{}]通过modbus读取{}的数据key={}", deviceType, model.getDeviceId(), addressCacheKey);
                }
                if (response.isEmpty()) {
                    return;
                }

                Data responseData = new Data(model.getByteOffset(), model.getBitOffset(), 1, model.getType(), model.isLittleEndian());
                response.parseData(responseData);
                String measureValue;
                Unit unit = unitMap.get(model.getUnitId());
                Unit dataUnit = unitMap.get(model.getDataUnitId());
                if (
                        !Objects.equals(model.getDataUnitId(), model.getUnitId()) &&
                                Objects.nonNull(unit) &&
                                Objects.nonNull(dataUnit)
                ) {
                    if (Objects.equals(DataEnum.BOOL, responseData.getDataType()) ||
                            Objects.equals(DataEnum.STRING, responseData.getDataType())) {
                        log.debug("[采集器][{}]通过缓存区读取{}的数据return了", deviceType, model.getDeviceId());
                        return;
                    } else if (Objects.equals(DataEnum.FLOAT32, responseData.getDataType()) ||
                            Objects.equals(DataEnum.FLOAT64, responseData.getDataType())) {
                        BigDecimal decimal = BigDecimal.valueOf(Double.valueOf(responseData.getValue())).setScale(4, RoundingMode.HALF_UP);
                        measureValue = unit.calculateFromUnit(decimal, dataUnit).toString();
                    } else {
                        BigDecimal decimal = BigDecimal.valueOf(Double.valueOf(responseData.getValue())).setScale(4, RoundingMode.HALF_UP);
                        measureValue = unit.calculateFromUnit(decimal, dataUnit).toString();
                    }
                } else {
                    measureValue = responseData.getValue();
                }
                log.debug("[采集器][{}]读取{}的属性{}的值{}", deviceType, model.getDeviceId(), model.getDataTypeId(), measureValue);
                if (Objects.equals(1, model.getNotifyType())) {
                    // 变化通知
                    String key = model.getId();
                    String oldValue = GLOBAL_CACHE.get(key);
                    GLOBAL_CACHE.put(key, measureValue);
                    if (!Objects.equals(measureValue, oldValue)) {
                        MeasureDataBean bean = new MeasureDataBean();
                        bean.setMeasureTime(time);
                        bean.setMeasureValue(measureValue);
                        bean.setDataTypeId(model.getDataTypeId());
                        bean.setDeviceId(model.getDeviceId());
                        bean.setUnitId(model.getUnitId());
                        dataList.add(bean);
                    }
                } else {
                    MeasureDataBean bean = new MeasureDataBean();
                    bean.setMeasureTime(time);
                    bean.setMeasureValue(measureValue);
                    bean.setDataTypeId(model.getDataTypeId());
                    bean.setDeviceId(model.getDeviceId());
                    bean.setUnitId(model.getUnitId());
                    dataList.add(bean);
                }
            } catch (Exception e) {
                log.error(e.getLocalizedMessage());
            }
        });

        if (CollectionUtil.isEmpty(dataList)) {
            return;
        }

        mqttClientTemplate.publishPayload(topic + "/" + deviceType, dataList);
        if (context.getNextFireTime().getTime() - context.getFireTime().getTime() >= 5 * 60 * 1000) {
            //超过五分钟的先断开长链接
            template.disconnect();
        }
    }

    private MqttClientTemplate mqttClientTemplate;

    @Autowired
    public void setMqttClientTemplate(MqttClientTemplate mqttClientTemplate) {
        this.mqttClientTemplate = mqttClientTemplate;
    }
}
